

#include "audio_processing_module.h"

#include "modules/audio_processing/include/audio_processing.h"
#include "common_audio/channel_buffer.h"

#include <iostream>


AudioProcessingModule::AudioProcessingModule(int aec_type, bool enable_ns, int agc_type, bool enable_vad) {
    system_delay = 0;

    ap = AudioProcessingBuilder().Create();


    AudioProcessing::Config config = ap->GetConfig();
    config.echo_canceller.enabled = true;
    if (2 == aec_type) {
        config.echo_canceller.enabled = true;
    }

    if (enable_ns) {
        config.noise_suppression.enabled = true;
        config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kLow;
    }

    if (agc_type) {
        config.gain_controller2.enabled = true;
        // Adaptive Digital AGC
        config.gain_controller2.adaptive_digital.enabled = true;
        config.gain_controller2.adaptive_digital.max_output_noise_level_dbfs = 30;
    }

    if (enable_vad) {
        config.voice_detection.enabled = true;
//        ap->voice_detection()->Enable(true);
//        ap->voice_detection()->set_likelihood(VoiceDetection::kVeryLowLikelihood);
//        ap->voice_detection()->set_frame_size_ms(frame_size_ms);
    }
    ap->ApplyConfig(config);

    nearend_config = new StreamConfig(default_rate, default_channels, false);
    nearend_filtered_config = new StreamConfig(default_rate, default_channels, false);

    int num_frames = nearend_config->num_frames();

    nearend_fbuf = new float[num_frames * default_channels];
    nearend_ibuf = new int16_t[num_frames * default_channels];
    nearend_cbuf = new ChannelBuffer<float>(num_frames, default_channels);
    nearend_filtered_cbuf = new ChannelBuffer<float>(num_frames, default_channels);

    farend_config = new StreamConfig(default_rate, default_channels, false);

    num_frames = farend_config->num_frames();

    farend_fbuf = new float[num_frames * default_channels];
    farend_cbuf = new ChannelBuffer<float>(num_frames, default_channels);
}

void AudioProcessingModule::set_stream_format(int rate, int channels, int out_rate, int out_channels) {
    delete nearend_fbuf;
    delete nearend_ibuf;
    delete nearend_cbuf;
    delete nearend_filtered_cbuf;

    nearend_config->set_sample_rate_hz(rate);
    nearend_config->set_num_channels(channels);

    nearend_filtered_config->set_sample_rate_hz(out_rate);
    nearend_filtered_config->set_num_channels(out_channels);

    int num_frames = nearend_config->num_frames();

    nearend_fbuf = new float[num_frames * channels];
    nearend_ibuf = new int16_t[num_frames * channels];
    nearend_cbuf = new ChannelBuffer<float>(num_frames, channels);
    nearend_filtered_cbuf = new ChannelBuffer<float>(num_frames, channels);
}

void AudioProcessingModule::set_reverse_stream_format(int rate, int channels) {
    delete farend_fbuf;
    delete farend_cbuf;

    farend_config->set_sample_rate_hz(rate);
    farend_config->set_num_channels(channels);

    int num_frames = farend_config->num_frames();

    farend_fbuf = new float[num_frames * channels];
    farend_cbuf = new ChannelBuffer<float>(num_frames, channels);
}

string AudioProcessingModule::process_stream(const string &nearend) {
    const int16_t *y = (const int16_t *) (nearend.data());
    int frames = nearend_config->num_frames();
    int channels = nearend_config->num_channels();

    S16ToFloat(y, frames * channels, nearend_fbuf);
    Deinterleave(nearend_fbuf, frames, channels, nearend_cbuf->channels());

    ap->ProcessStream(nearend_cbuf->channels(),
                      *nearend_config,
                      *nearend_filtered_config,
                      nearend_filtered_cbuf->channels());

    channels = nearend_filtered_config->num_channels();

    Interleave(nearend_filtered_cbuf->channels(), frames, channels, nearend_fbuf);
    FloatToS16(nearend_fbuf, frames * channels, nearend_ibuf);

    return string((const char *) nearend_ibuf, frames * channels * sizeof(int16_t));
}

void AudioProcessingModule::process_reverse_stream(const string &farend) {
    const int16_t *x = (const int16_t *) (farend.data());
    int frames = farend_config->num_frames();
    int channels = farend_config->num_channels();

    S16ToFloat(x, frames * channels, farend_fbuf);
    Deinterleave(farend_fbuf, frames, channels, farend_cbuf->channels());

    ap->ProcessReverseStream(farend_cbuf->channels(),
                             *farend_config,
                             *farend_config,
                             farend_cbuf->channels());

    ap->set_stream_delay_ms(system_delay);
}

void AudioProcessingModule::set_system_delay(int delay) {
    system_delay = delay;
}

bool AudioProcessingModule::has_echo() {

//    return ap->echo_cancellation()->stream_has_echo();
    return true;
}

bool AudioProcessingModule::has_voice() {
//    return ap->voice_detection()->stream_has_voice();
    return true;
}

int AudioProcessingModule::vad_level() {
//    return ap->voice_detection()->likelihood();
    return 0;
}

void AudioProcessingModule::set_vad_level(int level) {
    if (level < 0 || level > 3) {
        return;
    }
//    ap->voice_detection()->set_likelihood(static_cast<VoiceDetection::Likelihood>(level));
}

int AudioProcessingModule::agc_level() {
//    return ap->gain_control()->stream_analog_level();
    return 0;
}

void AudioProcessingModule::set_agc_level(int level) {
    if (level < 0 || level > 100) {
        return;
    }
//    ap->gain_control()->set_stream_analog_level(level);
}

void AudioProcessingModule::set_agc_target(int dbfs) {
    if (dbfs < 0) {
        dbfs = -dbfs;
    }

    if (dbfs > 31) {
        dbfs = 31;
    }

//    ap->gain_control()->set_target_level_dbfs(dbfs);
}

int AudioProcessingModule::ns_level() {

//    return ap->noise_suppression()->level();
    return 0;
}

void AudioProcessingModule::set_ns_level(int level) {
    if (level < 0 || level > 3) {
        return;
    }
//    ap->noise_suppression()->set_level(static_cast<NoiseSuppression::Level>(level));
}

int AudioProcessingModule::aec_level() {
//    return ap->echo_cancellation()->suppression_level();
    return 0;
}

void AudioProcessingModule::set_aec_level(int level) {
    if (level < 0 || level > 2) {
        return;
    }
//    ap->echo_cancellation()->set_suppression_level(static_cast<EchoCancellation::SuppressionLevel>(level));
}

AudioProcessingModule::~AudioProcessingModule() {
    delete nearend_config;
    delete farend_config;
    delete nearend_fbuf;
    delete nearend_ibuf;
    delete farend_fbuf;
    delete nearend_cbuf;
    delete nearend_filtered_cbuf;
    delete farend_cbuf;

    delete ap;
}
